/* 
 * File:    Server for "Tiny World of Warcraft" (TWW)
 * Authors: Jonathan Kotker (ee122-bw) and Shaan Mulchandani (ee122-bv)
 */

#define MIN(a, b) ((a) > (b) ? (b) : (a))
#define MAX(a, b) ((a) < (b) ? (b) : (a))

#include "bufferList.h"
#include "bufferListNode.h"
#include "constants.h"
#include "duplicate.h"
#include "duplicateList.h"
#include "fileio.h"
#include "messages.h"
#include "packetAssembler.h"
#include "packetContents.h"
#include "packetParser.h"
#include "playerList.h"
#include "playerStatistics.h"
#include "playerListNode.h"
#include "stringUtils.h"
#include "UDP_Assembler.h"
#include "UDP_Contents.h"
#include "UDP_Parser.h"

#include <arpa/inet.h>  /* for inet_ntoa() */
#include <ctype.h>      /* for isalnum() */
#include <errno.h>      /* for perror() */
#include <netinet/in.h> /* for struct sockaddr_in */
#include <signal.h>     /* for signal() */
#include <stdio.h>      /* for fprintf() */
#include <stdint.h>     /* for uint16_t, uint32_t, ... */
#include <stdlib.h>     /* for exit() */
#include <string.h>     /* for memset(), strstr() */
#include <sys/select.h> /* for select() */
#include <sys/socket.h> /* for socket() */
#include <sys/types.h>  /* for socket() */
#include <time.h>       /* for nanosleep() */
#include <unistd.h>
#include <netdb.h>     /* for getopt(), read(), write(), ... */

unsigned int calc_p2p_id(unsigned char* s) {
    unsigned int hashval;
    for (hashval = 0; *s != 0; s++)
        hashval = *s + (31 * hashval);
    return hashval % 1024;
}

void broadcastBytes(char*, int, int, bufferList*, playerList*,
        fd_set*, int*, int*);

void processClientPacket(packetContents*, int, int, bufferList*,
        playerList*, playerList*, fd_set*, int*, int*, int*, int*,
        duplicateList*, unsigned int);

void processUdpPacket(int, UDP_Contents*, struct sockaddr*,
        socklen_t, duplicateList*);

void done(playerList*, playerList*, duplicateList*, int);

/* Remove client with the given file descriptor from the arguments provided. */
void removeClient(int socketFd, int listener,
        bufferList* currentBuffers, playerList* currentPlayers,
        fd_set* masterFdSet, int* numConnections, int* maxFd) {
    FD_CLR(socketFd, masterFdSet);

    playerListNode* clientNode =
            accessPlayerFd(currentPlayers, socketFd);
    packetContents contents;

    if (clientNode != NULL) {
        // Prepare the LOGOUT_NOTIFY message to be broadcast, only if
        // clientNode is available. This is a special case for
        // invalid names.
        contents.messageType = LOGOUT_NOTIFY;
        strncpy(contents.victimName,
                clientNode->statistics->name, EE122_MAX_NAME_LENGTH);
        contents.victimName[EE122_MAX_NAME_LENGTH] = 0;
        contents.hp = clientNode->statistics->hp;
        contents.exp = clientNode->statistics->exp;
        contents.xLoc = clientNode->statistics->xLoc;
        contents.yLoc = clientNode->statistics->yLoc;

        // Remove the client from the player list.
        removePlayer(currentPlayers, clientNode->statistics->name);
    }

    // Remove the client buffer from the buffer list.
    removeBuffer(currentBuffers, socketFd);

    // Reduce the number of connections by one.
    (*numConnections) = (*numConnections) - 1;

    close(socketFd);

    if (clientNode != NULL) {
        // Broadcast the LOGOUT_NOTIFY message.
        char assembledPacket[300];
        int length = assemblePacket(assembledPacket, &contents);

        // Send everyone on server the LOGOUT_NOTIFY message.
        broadcastBytes(assembledPacket, length, listener,
                currentBuffers, currentPlayers, masterFdSet,
                numConnections, maxFd);
    }
};

/*
 * Sends the data in the `bytes' array. This method keeps sending until
 * all of the data has been sent, as the `send' method, by itself, does
 * not send all of the data in one go.
 */
void sendBytes(int socketFd, char *bytesToSend, int len, int listener,
        bufferList* currentBuffers, playerList* currentPlayers,
        fd_set* masterFdSet, int* numConnections, int* maxFd) {
    int bytesSent = 0, total = 0, bytesLeft = len;

    while (total < len) {
        bytesSent = send(socketFd, bytesToSend + total, bytesLeft, 0);
        if (bytesSent == -1) {
            // Connection to the client is broken.
            removeClient(socketFd, listener, currentBuffers,
                    currentPlayers, masterFdSet, numConnections, maxFd);
        }
        total += bytesSent;
        bytesLeft -= bytesSent;
    }
};

/*
 * Broadcast the given bytes to all clients.
 */
void broadcastBytes(char* bytesToSend, int len, int listener,
        bufferList* currentBuffers, playerList* currentPlayers,
        fd_set* masterFdSet, int* numConnections, int* maxFd) {
    int i;
    for (i = 0; i <= (*maxFd); i++) {
        if (FD_ISSET(i, masterFdSet) && (i != listener)
                // Also ensure that the player on the connection has logged in.
                && (accessPlayerFd(currentPlayers, i) != NULL)) {
            sendBytes(i, bytesToSend, len, listener, currentBuffers,
                    currentPlayers, masterFdSet, numConnections, maxFd);
        }
    }
};

/*
 * Process bytes coming into `udpListener', the file descriptor
 * corresponding to a UDP socket.
 */
void processUdpBytes(int udpListener, duplicateList* history) {
    // Buffer to store bytes obtained through the UDP listener.
    char buffer[MAX_RECV_LEN];
    struct sockaddr clientAddress; // Address from where data was received
    socklen_t clientAddressLength; // Length of clientAddress
    clientAddressLength = sizeof (clientAddress);

    int bytesRecvd = recvfrom(udpListener, buffer, MAX_RECV_LEN,
            0, &clientAddress, &clientAddressLength);
    if (bytesRecvd <= 0) {
        on_malformed_udp();
    } else if (buffer[0] == PLAYER_STATE_REQUEST) {
        if (bytesRecvd < 16) {
            on_malformed_udp();
        } else if (bytesRecvd >= 16) {
            UDP_Contents udpPacketContents;
            parseUdpPacket(buffer, &udpPacketContents);
            processUdpPacket(udpListener, &udpPacketContents,
                    &clientAddress, clientAddressLength, history);
        }
    } else if (buffer[0] == SAVE_STATE_REQUEST) {
        if (bytesRecvd < 28) {
            on_malformed_udp();
        } else if (bytesRecvd >= 28) {
            UDP_Contents udpPacketContents;
            parseUdpPacket(buffer, &udpPacketContents);
            processUdpPacket(udpListener, &udpPacketContents,
                    &clientAddress, clientAddressLength, history);
        }
    } else { // Incorrect message type.
        on_malformed_udp();
    }
}

/*
 * Process a packet received over a UDP socket, stored in the
 * `udpPacketContents' struct.
 */
void processUdpPacket(int udpListener, UDP_Contents* udpPacketContents,
        struct sockaddr* clientAddress, socklen_t clientAddressLength,
        duplicateList* history) {

    if (udpPacketContents->errorFlag == 1)
        on_malformed_udp();

    // Determine if this packet has been seen before;
    // if so, send the same response as before.
    duplicate checker;
    duplicate* result;

    checker.id = udpPacketContents->id;
    memcpy(&checker.server_ip_add, clientAddress, sizeof (*clientAddress));

    if ((result = existsInList(history, &checker)) != NULL) {
        sendto(udpListener, result->stored_response, result->response_length,
                0, clientAddress, clientAddressLength);
        return;
    }

    switch (udpPacketContents->messageType) {
        case PLAYER_STATE_REQUEST:
        {
            int length, i;
            playerStatistics stats;
            char assembledPacket[MAX_BUFFER_SIZE];
            for (i = 0; i < MAX_BUFFER_SIZE; i++)
                assembledPacket[i] = 0x00;
            duplicate* entry = (duplicate *) (malloc(sizeof (duplicate)));

            strncpy(stats.name, udpPacketContents->playerName,
                    EE122_MAX_NAME_LENGTH);
            readfromfile(&stats);
            udpPacketContents->messageType = PLAYER_STATE_RESPONSE;
            udpPacketContents->hp = stats.hp;
            udpPacketContents->exp = stats.exp;
            udpPacketContents->xLoc = stats.xLoc;
            udpPacketContents->yLoc = stats.yLoc;

            length = assembleUdpPacket(assembledPacket, udpPacketContents);
            sendto(udpListener, assembledPacket, length, 0,
                    clientAddress, clientAddressLength);

            entry->id = udpPacketContents->id;
            entry->response_length = length;
            // entry->server_ip_add = udpPacketContents->server_ip_add;
            memcpy(&entry->server_ip_add, clientAddress, sizeof (*clientAddress));
            memcpy(entry->stored_response, assembledPacket, MAX_BUFFER_SIZE);
            addDuplicateInfo(history, entry);

            break;
        }

        case SAVE_STATE_REQUEST:
        {
            int length, i;
            playerStatistics stats;
            char assembledPacket[MAX_BUFFER_SIZE];
            for (i = 0; i < MAX_BUFFER_SIZE; i++)
                assembledPacket[i] = 0x00;
            duplicate* entry = (duplicate *) (malloc(sizeof (duplicate)));

            udpPacketContents->errorCode = 0x01;
            strncpy(stats.name, udpPacketContents->playerName,
                    EE122_MAX_NAME_LENGTH);
            stats.hp = udpPacketContents->hp;
            stats.exp = udpPacketContents->exp;
            stats.xLoc = udpPacketContents->xLoc;
            stats.yLoc = udpPacketContents->yLoc;
            writetofile(&stats);

            udpPacketContents->errorCode = 0x00;
            udpPacketContents->messageType = SAVE_STATE_RESPONSE;
            length = assembleUdpPacket(assembledPacket, udpPacketContents);
            sendto(udpListener, assembledPacket, length, 0,
                    clientAddress, clientAddressLength);

            entry->id = udpPacketContents->id;
            entry->response_length = length;
            // entry->server_ip_add = udpPacketContents->server_ip_add;
            memcpy(&entry->server_ip_add, clientAddress, sizeof (*clientAddress));
            memcpy(entry->stored_response, assembledPacket, MAX_BUFFER_SIZE);
            addDuplicateInfo(history, entry);

            break;
        }
    }
};

/*
 * Process bytes coming into `socketFd'. The `currentBuffers' buffer list
 * keeps track of buffers for each active connection, and can be used for
 * collecting bytes until a complete packet is received. The `masterFdSet'
 * file descriptor set, the `playerList' player list, and the `numConnections'
 * integer is also provided so that, in the case of a broken connection,
 * the corresponding client is removed from both data structures. Finally,
 * the `listener' and the `maxFd' socket file descriptors are provided
 * for the purposes of broadcasting.
 */
void processTcpBytes(int socketFd, int listener,
        bufferList* currentBuffers, playerList* currentPlayers,
        playerList* backupPlayers, fd_set* masterFdSet, int* numConnections,
        int* maxFd, int* predSockFd, int* succSockFd,
        duplicateList* history, unsigned int p2p_id) {
    // Obtain the buffer corresponding to this file descriptor.
    bufferListNode* bufferNode = accessBuffer(currentBuffers, socketFd);

    if (bufferNode == NULL) {
        fprintf(stdout, "No buffer node available for client. \n");
        return;
    }

    int done = 0;

    char* buffer = bufferNode->buffer;
    int* startPos = &(bufferNode->incomingPosition);

    int bufferSize = MAX_RECV_BUFFER_LEN - (*startPos);
    int bytesRecvd = recv(socketFd, (buffer + (*startPos)), bufferSize, 0);
    (*startPos) = (*startPos) + bytesRecvd;

    while (!done) {
        if (bytesRecvd == 0) {
            // Client has suspended connection. Log client off.
            removeClient(socketFd, listener, currentBuffers,
                    currentPlayers, masterFdSet, numConnections, maxFd);
            done = 1;
        } else if ((*startPos) < 3) {
            // We have not received enough of the header yet. Do nothing.
            done = 1;
        } else {
            int length = (buffer[1] << 8);
            length = length | buffer[2];

            if ((*startPos) < length) {
                // We have not yet received the whole packet. Do nothing.
                done = 1;
            } else {
                // We have received the whole packet. Process the packet.
                char packet[MAX_SEND_LEN];

                int i;
                for (i = 0; i < length; i++) {
                    packet[i] = buffer[i];
                }

                for (i = length; i < (*startPos); i++) {
                    buffer[i - length] = buffer[i];
                }

                *startPos = (*startPos) - length;

                if ((*startPos) >= 3) {
                    // There may be another full packet waiting to be processed.
                    // Set the `done' flag accordingly.
                    int newLength = (buffer[1] << 8);
                    newLength = newLength | buffer[2];

                    done = 0;
                    bytesRecvd = bytesRecvd - length;
                } else {
                    done = 1;
                }

                packetContents contents;
                parsePacket(packet, &contents);

                if (contents.flag == 1) {
                    // Malformed client packet. Log the client off.
                    removeClient(socketFd, listener, currentBuffers,
                            currentPlayers, masterFdSet, numConnections, maxFd);
                    break;
                }

                // Process the packet from the client.
                processClientPacket(&contents, socketFd, listener,
                        currentBuffers, currentPlayers, backupPlayers,
                        masterFdSet, numConnections, maxFd,
                        predSockFd, succSockFd, history, p2p_id);
            }
        }
    }
};

/*
 * Process a packet received from the client, whose contents are
 * present in `contents', through the socket file descriptor `socketFd'.
 */
void processClientPacket(packetContents* contents, int socketFd, int listener,
        bufferList* currentBuffers, playerList* currentPlayers,
        playerList* backupPlayers, fd_set* masterFdSet,
        int* numConnections, int* maxFd, int* predSockFd,
        int* succSockFd, duplicateList* history, unsigned int p2p_id) {

    int length;
    char assembledPacket[1000];
    playerStatistics stats;

    switch (contents->messageType) {
        case LOGIN_REQUEST:
        {
            strncpy(stats.name, contents->victimName, EE122_MAX_NAME_LENGTH);

            if ((accessPlayerFd(currentPlayers, socketFd)) != NULL) {
                // Client has already logged in through the file descriptor.
                contents->errorCode = 1;
                contents->messageType = INVALID_STATE;

                length = assemblePacket(assembledPacket, contents);
                sendBytes(socketFd, assembledPacket, length, listener,
                        currentBuffers, currentPlayers, masterFdSet,
                        numConnections, maxFd);
            } else if ((accessPlayer(currentPlayers,
                    contents->victimName)) != NULL) {
                // There is a player already logged in with the same name.
                contents->errorCode = 1;
                contents->messageType = LOGIN_REPLY;

                length = assemblePacket(assembledPacket, contents);
                sendBytes(socketFd, assembledPacket, length, listener,
                        currentBuffers, currentPlayers, masterFdSet,
                        numConnections, maxFd);
            } else {
                // The player is not already logged in.

                // Add the player to the player list.
                addPlayer(currentPlayers, &stats, socketFd);

                // Send the client a LOGIN_REPLY with error code of 0.
                contents->errorCode = 0;
                contents->messageType = LOGIN_REPLY;

                length = assemblePacket(assembledPacket, contents);
                sendBytes(socketFd, assembledPacket, length, listener,
                        currentBuffers, currentPlayers, masterFdSet,
                        numConnections, maxFd);

                // Broadcast a MOVE_NOTIFY message to every player.
                contents->messageType = MOVE_NOTIFY;

                length = assemblePacket(assembledPacket, contents);
                broadcastBytes(assembledPacket, length,
                        listener, currentBuffers, currentPlayers,
                        masterFdSet, numConnections, maxFd);

                // Send the new player a MOVE_NOTIFY message for each
                // of the existing players.
                char victimName[EE122_MAX_NAME_LENGTH + 1];
                strncpy(victimName, stats.name, EE122_MAX_NAME_LENGTH);

                playerListNode* currentPlayer = currentPlayers->head;
                while (currentPlayer) {
                    playerStatistics* playerStats = currentPlayer->statistics;

                    // Do not notify the victim about her/himself!
                    if ((strcmp(playerStats->name, victimName)) != 0) {
                        strncpy(stats.name, playerStats->name,
                                EE122_MAX_NAME_LENGTH);
                        readfromfile(&stats);

                        strncpy(contents->victimName, stats.name,
                                EE122_MAX_NAME_LENGTH);
                        contents->hp = stats.hp;
                        contents->exp = stats.exp;
                        contents->xLoc = stats.xLoc;
                        contents->yLoc = stats.yLoc;

                        length = assemblePacket(assembledPacket, contents);
                        sendBytes(socketFd, assembledPacket, length, listener,
                                currentBuffers, currentPlayers, masterFdSet,
                                numConnections, maxFd);
                    }
                    currentPlayer = currentPlayer->next;
                }
            }

            break;
        }
        case MOVE:
        {
            playerListNode* sender = accessPlayerFd(currentPlayers, socketFd);

            if (sender == NULL) {
                // Player is trying to move before logging in.
                contents->errorCode = 0;
                contents->messageType = INVALID_STATE;

                length = assemblePacket(assembledPacket, contents);
                sendBytes(socketFd, assembledPacket, length, listener,
                        currentBuffers, currentPlayers, masterFdSet,
                        numConnections, maxFd);
            } else {
                strncpy(stats.name, sender->statistics->name,
                        EE122_MAX_NAME_LENGTH);
                strncpy(contents->victimName, stats.name,
                        EE122_MAX_NAME_LENGTH);

                readfromfile(&stats);

                if (contents->moveDir == NORTH) {
                    stats.yLoc = stats.yLoc - 3;
                    if (stats.yLoc < 0)
                        stats.yLoc += 100;
                    else if (stats.yLoc > 99)
                        stats.yLoc %= 100;
                } else if (contents->moveDir == SOUTH) {
                    stats.yLoc = stats.yLoc + 3;
                    if (stats.yLoc < 0)
                        stats.yLoc += 100;
                    else if (stats.yLoc > 99)
                        stats.yLoc %= 100;
                } else if (contents->moveDir == WEST) {
                    stats.xLoc = stats.xLoc - 3;
                    if (stats.xLoc < 0)
                        stats.xLoc += 100;
                    else if (stats.xLoc > 99)
                        stats.xLoc %= 100;
                } else if (contents->moveDir == EAST) {
                    stats.xLoc = stats.xLoc + 3;
                    if (stats.xLoc < 0)
                        stats.xLoc += 100;
                    else if (stats.xLoc > 99)
                        stats.xLoc %= 100;
                }

                writetofile(&stats);

                contents->xLoc = stats.xLoc;
                contents->yLoc = stats.yLoc;
                contents->hp = stats.hp;
                contents->exp = stats.exp;
                contents->messageType = MOVE_NOTIFY;

                length = assemblePacket(assembledPacket, contents);
                // Send everyone on server the MOVE_NOTIFY message.
                broadcastBytes(assembledPacket, length, listener,
                        currentBuffers, currentPlayers, masterFdSet,
                        numConnections, maxFd);
            }
            break;
        }

        case ATTACK:
        {
            playerListNode* sender = accessPlayerFd(currentPlayers, socketFd);

            if (sender == NULL) {
                // Player is trying to attack before logging in.
                contents->errorCode = 0;
                contents->messageType = INVALID_STATE;

                length = assemblePacket(assembledPacket, contents);
                sendBytes(socketFd, assembledPacket, length, listener,
                        currentBuffers, currentPlayers, masterFdSet,
                        numConnections, maxFd);
            } else {
                strncpy(stats.name, sender->statistics->name,
                        EE122_MAX_NAME_LENGTH);
                strncpy(contents->attackerName, stats.name,
                        EE122_MAX_NAME_LENGTH);

                readfromfile(&stats);

                playerStatistics victimStats;
                strncpy(victimStats.name, contents->victimName,
                        EE122_MAX_NAME_LENGTH);
                readfromfile(&victimStats);

                if (strcmp(contents->victimName, contents->attackerName) == 0) {
                    // Do nothing.
                } else if ((accessPlayer(currentPlayers,
                        contents->victimName)) == NULL) {
                    // Do nothing.
                } else {
                    contents->damage = (rand() % 10) + 10;
                    if (contents->damage > victimStats.hp) {
                        contents->damage = victimStats.hp;
                    }

                    victimStats.hp = victimStats.hp - contents->damage;

                    // Add experience to attacker.
                    stats.exp = stats.exp + contents->damage;
                    writetofile(&stats);

                    contents->messageType = ATTACK_NOTIFY;
                    contents->hp = victimStats.hp;

                    writetofile(&victimStats);

                    length = assemblePacket(assembledPacket, contents);
                    // Send everyone on server the ATTACK_NOTIFY message.
                    broadcastBytes(assembledPacket, length, listener,
                            currentBuffers, currentPlayers, masterFdSet,
                            numConnections, maxFd);

                    if (victimStats.hp == 0) {
                        victimStats.hp = (rand() % 30) + 20;
                        contents->hp = victimStats.hp;

                        victimStats.xLoc = (rand() % 99);
                        contents->xLoc = victimStats.xLoc;
                        victimStats.yLoc = (rand() % 99);
                        contents->yLoc = victimStats.yLoc;

                        writetofile(&victimStats);

                        contents->messageType = MOVE_NOTIFY;

                        length = assemblePacket(assembledPacket, contents);
                        // Send everyone on server the MOVE_NOTIFY message.
                        broadcastBytes(assembledPacket, length, listener,
                                currentBuffers, currentPlayers, masterFdSet,
                                numConnections, maxFd);
                    }
                }
            }
            break;
        }

        case SPEAK:
        {
            playerListNode* sender = accessPlayerFd(currentPlayers, socketFd);

            if (sender == NULL) {
                // Player is trying to speak before logging in.
                contents->errorCode = 0;
                contents->messageType = INVALID_STATE;

                length = assemblePacket(assembledPacket, contents);
                sendBytes(socketFd, assembledPacket, length, listener,
                        currentBuffers, currentPlayers, masterFdSet,
                        numConnections, maxFd);
            } else {
                strncpy(stats.name, sender->statistics->name,
                        EE122_MAX_NAME_LENGTH);
                strncpy(contents->victimName, stats.name,
                        EE122_MAX_NAME_LENGTH);

                contents->messageType = SPEAK_NOTIFY;

                length = assemblePacket(assembledPacket, contents);
                // Send everyone on server the SPEAK_NOTIFY message.
                broadcastBytes(assembledPacket, length, listener,
                        currentBuffers, currentPlayers, masterFdSet,
                        numConnections, maxFd);
            }
            break;
        }

        case LOGOUT:
        {
            playerListNode* sender = accessPlayerFd(currentPlayers, socketFd);

            if (sender == NULL) {
                // Player is trying to log out before logging in.
                contents->errorCode = 0;
                contents->messageType = INVALID_STATE;

                length = assemblePacket(assembledPacket, contents);
                sendBytes(socketFd, assembledPacket, length, listener,
                        currentBuffers, currentPlayers, masterFdSet,
                        numConnections, maxFd);
            } else {
                strncpy(stats.name, sender->statistics->name,
                        EE122_MAX_NAME_LENGTH);
                strncpy(contents->victimName, stats.name,
                        EE122_MAX_NAME_LENGTH);

                removeClient(socketFd, listener, currentBuffers,
                        currentPlayers, masterFdSet, numConnections, maxFd);
            }
            break;
        }

        case P2P_JOIN_REQUEST:
        {

            // Determine new successor and predecessor
            // Read from peers.lst
            

            FILE *fp = NULL;
            char fileName[12];
            strncpy(fileName, "./peers.lst", 12);
            fileName[11] = '\0';
            fp = fopen(fileName, "a+");

            int i, j, p2p_flag;
            // If p2p_flag becomes 1, then the P2P_ID has been identified in the file.
            // If p2p_flag becomes 2, the server's saved p2p_id doesn't match the tcp/ip
            p2p_flag = 0;

            unsigned long int p2p;
            unsigned char ip[256];
            unsigned short int tcp;

            // Variables for Predecessor and Successor P2P_IDs
            unsigned int predec_p2p_id, succ_p2p_id;
            unsigned short int predec_tcp, succ_tcp;

            unsigned char predec_ip[256];
            unsigned char succ_ip[256];

            struct sockaddr_in pre_sockarr, succ_sockarr;

            // Initialize the variables.
            predec_p2p_id = succ_p2p_id = 0;
            // predec_ip = succ_ip = NULL;
            predec_tcp = succ_tcp = 0;

            // We need these 6 arrays to figure out our predecessor & successor when
            // entering the network. Obvious assumption: peers.lst is unsorted.
            // Another assumption: There aren't more than 50 servers!
            unsigned int lesser_than[50], greater_than[50]; // This is for P2P_IDs
            unsigned char lesser_ip[50][256];
            unsigned char greater_ip[50][256];
            unsigned short int lesser_tcp[50], greater_tcp[50];

            // Clear the arrays.
            for (i = 0; i < 50; i++) {
                lesser_than[i] = greater_than[i] = 0;
                // lesser_ip[i] = greater_ip[i] = NULL;
                lesser_tcp[i] = greater_tcp[i] = 0;
            }
            // These i & j are counters for determining the correct predec. & succ.
            i = j = 0;


            

            while (fscanf(fp, "%lu %s %hu\n", &p2p, ip, &tcp) != EOF) {
                if (p2p < p2p_id) {
                    lesser_than[i] = p2p;
                    strncpy(lesser_ip[i], ip, 256);
                    lesser_tcp[i] = tcp;
                    i++;
                } else if (p2p > p2p_id) {
                    greater_than[j] = p2p;
                    strncpy(greater_ip[j], ip, 256);
                    greater_tcp[j] = tcp;
                    j++;
                }

                if (p2p_flag == 2) {
                    fclose(fp);
                    done(currentPlayers, backupPlayers, history, EXIT_FAILURE);
                }
            }

            fclose(fp);

            // Now we have read from the file and populated the arrays.
            // Create a temp variable for use in for loops.
            int temp;

            predec_p2p_id = lesser_than[0];
            strncpy(predec_ip, lesser_ip[0], 256);
            predec_tcp = lesser_tcp[0];
            for (temp = 1; temp < i; temp++) {
                if (predec_p2p_id != lesser_than[temp]) {
                    predec_p2p_id = MAX(predec_p2p_id, lesser_than[temp]);
                    strncpy(predec_ip, lesser_ip[temp], 256);
                    predec_tcp = lesser_tcp[temp];
                }
            }

            succ_p2p_id = greater_than[0];
            strncpy(succ_ip, greater_ip[0], 256);
            succ_tcp = greater_tcp[0];
            for (temp = 1; temp < j; temp++) {
                if (succ_p2p_id != greater_than[temp]) {
                    succ_p2p_id = MIN(succ_p2p_id, greater_than[temp]);
                    strncpy(succ_ip, greater_ip[temp], 256);
                    succ_tcp = greater_tcp[temp];
                }
            }

            // This means we are the lowest P2P_ID
            if (i == 0) {
                predec_p2p_id = greater_than[0];
                strncpy(predec_ip, greater_ip[0], 256);
                predec_tcp = greater_tcp[0];
                for (temp = 1; temp < j; temp++) {
                    if (predec_p2p_id != greater_than[temp]) {
                        predec_p2p_id = MAX(predec_p2p_id, greater_than[temp]);
                        strncpy(predec_ip, greater_ip[temp], 256);
                        predec_tcp = greater_tcp[temp];
                    }
                }
            }

            // This means we are the highest P2P_ID
            if (j == 0) {
                succ_p2p_id = lesser_than[0];
                strncpy(succ_ip, lesser_ip[0], 256);
                succ_tcp = lesser_tcp[0];
                for (temp = 1; temp < i; temp++) {
                    if (succ_p2p_id != lesser_than[temp]) {
                        succ_p2p_id = MIN(succ_p2p_id, lesser_than[temp]);
                        strncpy(succ_ip, lesser_ip[temp], 256);
                        succ_tcp = lesser_tcp[temp];
                    }
                }
            }

            // Get the numeric versions of the IP addresses.
            inet_aton(predec_ip, &pre_sockarr.sin_addr);
            inet_aton(succ_ip, &succ_sockarr.sin_addr);



            

            if (((*succSockFd) == (*predSockFd)) ||
                    (strcmp(predec_ip, succ_ip) == 0)) {
                // Modify primary and backup range.
                // Find which players should now be stored on predecessor.
                clearList(backupPlayers);
                playerListNode* current;
                unsigned int player_p2p_id;

                for (current = currentPlayers->head;
                        current != NULL; current = current->next) {
                    // p2p_id is our current server's p2p id.
                    // server_p2p_id is the new entry's p2p id.
                    // If player_p2p_id < server_p2p_id,
                    // then move the player to backup list.
                    // Else, keep the player on the same server.
                    player_p2p_id = calc_p2p_id(current->statistics->name);
                    if (player_p2p_id < contents->server_p2p_id) {
                        addPlayer(backupPlayers, current->statistics,
                                current->fileDescriptor);
                        removePlayer(currentPlayers, current->statistics->name);
                    }
                }

                // Send P2P join request from previous step.
                contents->user_number = countPlayers(backupPlayers);
                contents->messageType = P2P_JOIN_RESPONSE;
                contents->playerData = backupPlayers;

                length = assemblePacket(assembledPacket, contents);
                sendBytes(socketFd, assembledPacket, length, listener,
                        currentBuffers, currentPlayers, masterFdSet,
                        numConnections, maxFd);

                // Send back all data in primary range.
                contents->user_number = countPlayers(currentPlayers);
                contents->messageType = P2P_JOIN_RESPONSE;
                contents->playerData = currentPlayers;

                length = assemblePacket(assembledPacket, contents);
                sendBytes(socketFd, assembledPacket, length, listener,
                        currentBuffers, currentPlayers, masterFdSet,
                        numConnections, maxFd);

                // Close connection with previous successor.
                if ((*succSockFd) != 0) {
                    FD_CLR((*succSockFd), masterFdSet);
                    removeBuffer(currentBuffers, (*succSockFd));
                    (*numConnections) = (*numConnections) - 1;
                    close((*succSockFd));
                }

                // Connect to new successor's TCP port and maintain connection.
                *succSockFd = socket(PF_INET, SOCK_STREAM, 0);
                if (*succSockFd < 0) {
                    fprintf(stdout, "!socket: Cannot create TCP socket for new successor. \n");
                    done(currentPlayers, backupPlayers, history, EXIT_FAILURE);
                }

                // Read from peers.lst
                FILE *fp;
                char fileName[12];
                strncpy(fileName, "./peers.lst", 12);
                fileName[11] = '\0';
                fp = fopen(fileName, "r");

                struct sockaddr_in succAddr;

                unsigned long int p2p;
                unsigned char ip[256];
                unsigned short int tcp;

                while (fscanf(fp, "%lu %s %hu \n", &p2p, ip, &tcp) != EOF) {
                    if (p2p == contents->server_p2p_id) {

                        inet_aton(ip, &succAddr.sin_addr);

                        struct sockaddr_in succTcpSin;

                        memset(&succTcpSin, 0, sizeof (succTcpSin));
                        succTcpSin.sin_family = AF_INET;
                        succTcpSin.sin_addr.s_addr =
                                htonl(succAddr.sin_addr.s_addr);
                        succTcpSin.sin_port = htons(tcp);

                        /* if (connect((*succSockFd),
                                (struct sockaddr *) & succTcpSin,
                                sizeof (succTcpSin)) == 0) {
                            fprintf(stdout,
                                    "!connect: Cannot connect to predecessor. \n");
                            done(currentPlayers, backupPlayers, history, EXIT_FAILURE);
                        } */

                        FD_SET(*succSockFd, masterFdSet);
                        (*maxFd) = MAX((*maxFd), (*succSockFd));
                        addBuffer(currentBuffers, (*succSockFd));
                        (*numConnections)++;

                        break;
                    }
                }

                fclose(fp);
            } else if (predec_p2p_id == contents->server_p2p_id) {
                // I am the predecessor.

                // Send back all data in primary range.
                contents->user_number = countPlayers(currentPlayers);
                contents->messageType = P2P_JOIN_RESPONSE;
                contents->playerData = currentPlayers;

                length = assemblePacket(assembledPacket, contents);
                sendBytes(socketFd, assembledPacket, length, listener,
                        currentBuffers, currentPlayers, masterFdSet,
                        numConnections, maxFd);

                // Close connection with previous successor.
                if ((*succSockFd) != 0) {
                    FD_CLR((*succSockFd), masterFdSet);
                    removeBuffer(currentBuffers, (*succSockFd));
                    (*numConnections) = (*numConnections) - 1;
                    close((*succSockFd));
                }

                // Connect to new successor's TCP port and maintain connection.
                *succSockFd = socket(PF_INET, SOCK_STREAM, 0);
                if (*succSockFd < 0) {
                    fprintf(stdout, "!socket: Cannot create TCP socket for new successor. \n");
                    done(currentPlayers, backupPlayers, history, EXIT_FAILURE);
                }

                FD_SET(*succSockFd, masterFdSet);
                (*maxFd) = MAX((*maxFd), (*succSockFd));
                addBuffer(currentBuffers, (*succSockFd));
                (*numConnections)++;

                // Read from peers.lst
                FILE *fp;
                char fileName[12];
                strncpy(fileName, "./peers.lst", 12);
                fileName[11] = '\0';
                fp = fopen(fileName, "r");

                struct sockaddr_in succAddr;

                unsigned long int p2p;
                unsigned char ip[256];
                unsigned short int tcp;

                while (fscanf(fp, "%lu %s %hu \n", &p2p, ip, &tcp) != EOF) {
                    if (p2p == contents->server_p2p_id) {
                        inet_aton(ip, &succAddr.sin_addr);

                        struct sockaddr_in succTcpSin;

                        memset(&succTcpSin, 0, sizeof (succTcpSin));
                        succTcpSin.sin_family = AF_INET;
                        succTcpSin.sin_addr.s_addr =
                                htonl(succAddr.sin_addr.s_addr);
                        succTcpSin.sin_port = htons(tcp);

                        /* if (connect((*succSockFd),
                                (struct sockaddr *) & succTcpSin,
                                sizeof (succTcpSin)) < 0) {
                            fprintf(stdout,
                                    "!connect: Cannot connect to predecessor. \n");
                            done(currentPlayers, backupPlayers, history, EXIT_FAILURE);
                        } */

                        break;
                    }
                }

                fclose(fp);
            } else if (succ_p2p_id == contents->server_p2p_id) {
                // I am the successor.

                // Modify primary and backup range.
                // Find which players should now be stored on predecessor.
                clearList(backupPlayers);
                playerListNode* current;
                unsigned int player_p2p_id;

                for (current = currentPlayers->head;
                        current != NULL; current = current->next) {
                    // p2p_id is our current server's p2p id.
                    // server_p2p_id is the new entry's p2p id.
                    // If player_p2p_id < server_p2p_id,
                    // then move the player to backup list.
                    // Else, keep the player on the same server.
                    player_p2p_id = calc_p2p_id(current->statistics->name);
                    if (player_p2p_id < contents->server_p2p_id) {
                        addPlayer(backupPlayers, current->statistics,
                                current->fileDescriptor);
                        removePlayer(currentPlayers, current->statistics->name);
                    }
                }

                // Send P2P join request from previous step.
                contents->user_number = countPlayers(backupPlayers);
                contents->messageType = P2P_JOIN_RESPONSE;
                contents->playerData = backupPlayers;

                length = assemblePacket(assembledPacket, contents);
                sendBytes(socketFd, assembledPacket, length, listener,
                        currentBuffers, currentPlayers, masterFdSet,
                        numConnections, maxFd);
            }

            break;
        }

        case P2P_JOIN_RESPONSE:
        {
            // Close connection with predecessor.
            if (((*predSockFd) != 0) && ((*predSockFd) != (*succSockFd))) {
                FD_CLR((*predSockFd), masterFdSet);
                removeBuffer(currentBuffers, (*predSockFd));
                (*numConnections) = (*numConnections) - 1;
                close((*predSockFd));
            }

            break;
        }

        case P2P_BKUP_REQUEST:
        {
            contents->errorCode = 0x02;
            if (accessPlayer(backupPlayers, contents->victimName) == NULL) {
                playerStatistics stats;
                strncpy(stats.name, contents->victimName,
                        EE122_MAX_NAME_LENGTH);
                stats.hp = contents->hp;
                stats.exp = contents->exp;
                stats.xLoc = contents->xLoc;
                stats.yLoc = contents->yLoc;
                addPlayer(backupPlayers, &stats, 0);
                contents->errorCode = 0x00;
            } else {
                playerStatistics stats;
                strncpy(stats.name, contents->victimName,
                        EE122_MAX_NAME_LENGTH);
                stats.hp = contents->hp;
                stats.exp = contents->exp;
                stats.xLoc = contents->xLoc;
                stats.yLoc = contents->yLoc;
                removePlayer(backupPlayers, contents->victimName);
                addPlayer(backupPlayers, &stats, 0);
                contents->errorCode = 0x00;
            }

            contents->messageType = P2P_BKUP_RESPONSE;
            length = assemblePacket(assembledPacket, contents);
            sendBytes(socketFd, assembledPacket, length, listener,
                    currentBuffers, backupPlayers, masterFdSet,
                    numConnections, maxFd);

            break;
        }
    }
};

/*
 * Tasks to be run before exiting the program. `playerList' is the list
 * of current players, and `status' is the reason the program is exiting.
 */
void done(playerList* currentPlayers, playerList* backupPlayers,
        duplicateList* duplicates, int status) {
    clearList(currentPlayers);
    clearList(backupPlayers);
    clearDuplicateList(duplicates);
    exit(status);
};

int main(int argc, char** argv) {
    // Set up variables to be used throughout the execution of the program.
    // The primary list of players that this server hosts
    playerList currentPlayers;
    currentPlayers.head = NULL;

    /* The secondary list of players that are maintained as a backup for a
       successor server */
    playerList backupPlayers;
    backupPlayers.head = NULL;
    bufferList currentBuffers;
    currentBuffers.head = NULL;
    duplicateList history;
    initDuplicateList(&history);

    struct timeval timeout;  /* Timeout for select */


    if (argc != 5 || (strcmp(argv[1], "-t") != 0) || (strcmp(argv[3], "-u"))) {
        // Not enough arguments.
        fprintf(stdout,
                "Expected command-line argument format: -t <Server TCP port> -u <Server UDP port> \n");
        done(&currentPlayers, &backupPlayers, &history, EXIT_FAILURE);
    }

    // Obtain the P2P ID of the server.
    unsigned int p2p_id;
    unsigned int server_ip;
    char* ip_dots_and_numbers;

    char hostname[128];
    gethostname(hostname, sizeof hostname);

    struct hostent *he;
    struct in_addr **addr_list;
    if ((he = gethostbyname(hostname)) == NULL) {
        fprintf(stdout, "!gethostbyname: Cannot determine host from name");
        done(&currentPlayers, &backupPlayers, &history, EXIT_FAILURE);
    }

    addr_list = (struct in_addr **) he->h_addr_list;
    // Get the first IP address from the list;
    // this is the current machine's IP address.
    struct in_addr first = *(addr_list[0]);
    server_ip = first.s_addr;
    ip_dots_and_numbers = inet_ntoa(first);

    // Parse command-line arguments.
    int tcpPort = atoi(argv[2]);
    int udpPort = atoi(argv[4]);

    // Now that we have server IP and TCP port, calculate P2P_ID
    unsigned char p2p_array[7];
    *((unsigned int *) (p2p_array)) = server_ip;
    *((unsigned short int *) (p2p_array + 4)) = ntohs(tcpPort);
    p2p_array[6] = 0x00;

    p2p_id = calc_p2p_id(p2p_array);

    // Get listener TCP socket file descriptor.
    int tcpListener = socket(PF_INET, SOCK_STREAM, 0);
    if (tcpListener < 0) {
        // Unable to create connection
        fprintf(stdout, "!socket: Cannot create TCP socket on server. \n");
        done(&currentPlayers, &backupPlayers, &history, EXIT_FAILURE);
    }

    // Override TCP's 2MSL constraint.
    int on = 1;
    if (setsockopt(tcpListener, SOL_SOCKET,
            SO_REUSEADDR, &on, sizeof (on)) < 0) {
        // Unable to create connection
        fprintf(stdout, "!setsockopt: Cannot override 2MSL constraint. \n");
        done(&currentPlayers, &backupPlayers, &history, EXIT_FAILURE);
    }

    // Bind TCP listener to provided port.
    struct sockaddr_in tcpSin, newTcpSin;
    int newTcpSinSize = sizeof (newTcpSin);

    memset(&tcpSin, 0, sizeof (tcpSin));
    tcpSin.sin_family = AF_INET;
    tcpSin.sin_addr.s_addr = htonl(INADDR_ANY);
    tcpSin.sin_port = htons(tcpPort);
    if (bind(tcpListener, (struct sockaddr *) & tcpSin, sizeof (tcpSin)) < 0) {
        fprintf(stdout, message_on_server_port_bind_failure());
        done(&currentPlayers, &backupPlayers, &history, EXIT_FAILURE);
    }

    // Listen on provided port; allow a backlog of 20 incoming connections.
    if (listen(tcpListener, 20) < 0) {
        fprintf(stdout, "!listen: Cannot listen on provided port %d. \n");
        done(&currentPlayers, &backupPlayers, &history, EXIT_FAILURE);
    }

    // Get listener UDP socket file descriptor.
    int udpListener = socket(PF_INET, SOCK_DGRAM, 0);
    if (udpListener < 0) {
        // Unable to create connection
        fprintf(stdout, "!socket: Cannot create UDP socket on server. \n");
        done(&currentPlayers, &backupPlayers, &history, EXIT_FAILURE);
    }

    // Bind UDP listener to provided port.
    struct sockaddr_in udpSin;

    memset(&udpSin, 0, sizeof (udpSin));
    udpSin.sin_family = AF_INET;
    udpSin.sin_addr.s_addr = htonl(INADDR_ANY);
    udpSin.sin_port = htons(udpPort);
    if (bind(udpListener, (struct sockaddr *) & udpSin, sizeof (udpSin)) < 0) {
        fprintf(stdout, message_on_server_port_bind_failure());
        done(&currentPlayers, &backupPlayers, &history, EXIT_FAILURE);
    }

    // Set up variables for infinite loop.
    fd_set masterReadFds; // Master file descriptor list.
    fd_set readFds; // Temporary file descriptor list.

    // Keep track of connections; maximum is 20.
    int numConnections = 0;
    // Also keep track of largest socket file descriptor.
    int maxSocketFd = 0;

    // Predecessor file descriptor.
    int predSocketFd = 0;
    // Successor file descriptor.
    int succSocketFd = 0;

    // Zero out the read file descriptor sets.
    FD_ZERO(&masterReadFds);
    FD_ZERO(&readFds);

    // Add the listeners to the master set.
    FD_SET(tcpListener, &masterReadFds);
    FD_SET(udpListener, &masterReadFds);

    // Keep track of the maximum; so far, it is one of the listeners.
    maxSocketFd = MAX(tcpListener, udpListener);

    // Finish server setup.
    // Read from peers.lst
    FILE *fp = NULL;
    char fileName[12];
    strncpy(fileName, "./peers.lst", 12);
    fileName[11] = '\0';
    fp = fopen(fileName, "a+");

    int i, j, p2p_flag;
    // If p2p_flag becomes 1, then the P2P_ID has been identified in the file.
    // If p2p_flag becomes 2, the server's saved p2p_id doesn't match the tcp/ip
    p2p_flag = 0;

    unsigned long int p2p;
    unsigned char ip[256];
    unsigned short int tcp;

    // Variables for Predecessor and Successor P2P_IDs
    unsigned int predec_p2p_id, succ_p2p_id;
    unsigned short int predec_tcp, succ_tcp;

    unsigned char predec_ip[256];
    unsigned char succ_ip[256];

    struct sockaddr_in pre_sockarr, succ_sockarr;

    // Initialize the variables.
    predec_p2p_id = succ_p2p_id = 0;
    // predec_ip = succ_ip = NULL;
    predec_tcp = succ_tcp = 0;

    // We need these 6 arrays to figure out our predecessor & successor when
    // entering the network. Obvious assumption: peers.lst is unsorted.
    // Another assumption: There aren't more than 50 servers!
    unsigned int lesser_than[50], greater_than[50]; // This is for P2P_IDs
    unsigned char lesser_ip[50][256];
    unsigned char greater_ip[50][256];
    unsigned short int lesser_tcp[50], greater_tcp[50];

    // Clear the arrays.
    for (i = 0; i < 50; i++) {
        lesser_than[i] = greater_than[i] = 0;
        // lesser_ip[i] = greater_ip[i] = NULL;
        lesser_tcp[i] = greater_tcp[i] = 0;
    }
    // These i & j are counters for determining the correct predec. & succ.
    i = j = 0;

    while (fscanf(fp, "%lu %s %hu\n", &p2p, ip, &tcp) != EOF) {
        if (p2p < p2p_id) {
            lesser_than[i] = p2p;
            strncpy(lesser_ip[i], ip, 256);
            lesser_tcp[i] = tcp;
            i++;
        } else if (p2p > p2p_id) {
            greater_than[j] = p2p;
            strncpy(greater_ip[j], ip, 256);
            greater_tcp[j] = tcp;
            j++;
        } else if (p2p == p2p_id) {
            p2p_flag = 1;
            if (strcmp(ip, ip_dots_and_numbers) != 0) p2p_flag = 2;
            if (tcp != tcpPort) p2p_flag = 2;
        }

        if (p2p_flag == 2) {
            fclose(fp);
            done(&currentPlayers, &backupPlayers, &history, EXIT_FAILURE);
        }
    }

    // It is obvious in this case that our own P2P_ID was never found.
    if (p2p_flag == 0) {
        fprintf(fp, "%lu ", p2p_id);
        fprintf(fp, "%s ", ip_dots_and_numbers);
        fprintf(fp, "%hu", tcpPort);
        fprintf(fp, "\n");
    }

    fclose(fp);

    // Now we have read from the file and populated the arrays.
    // Create a temp variable for use in for loops.
    int temp;

    predec_p2p_id = lesser_than[0];
    strncpy(predec_ip, lesser_ip[0], 256);
    predec_tcp = lesser_tcp[0];
    for (temp = 1; temp < i; temp++) {
        if (predec_p2p_id != lesser_than[temp]) {
            predec_p2p_id = MAX(predec_p2p_id, lesser_than[temp]);
            strncpy(predec_ip, lesser_ip[temp], 256);
            predec_tcp = lesser_tcp[temp];
        }
    }

    succ_p2p_id = greater_than[0];
    strncpy(succ_ip, greater_ip[0], 256);
    succ_tcp = greater_tcp[0];
    for (temp = 1; temp < j; temp++) {
        if (succ_p2p_id != greater_than[temp]) {
            succ_p2p_id = MIN(succ_p2p_id, greater_than[temp]);
            strncpy(succ_ip, greater_ip[temp], 256);
            succ_tcp = greater_tcp[temp];
        }
    }

    // We have the lowest P2P_ID
    if (i == 0) {
        predec_p2p_id = greater_than[0];
        strncpy(predec_ip, greater_ip[0], 256);
        predec_tcp = greater_tcp[0];
        for (temp = 1; temp < j; temp++) {
            if (predec_p2p_id != greater_than[temp]) {
                predec_p2p_id = MAX(predec_p2p_id, greater_than[temp]);
                strncpy(predec_ip, greater_ip[temp], 256);
                predec_tcp = greater_tcp[temp];
            }
        }
    }

    // We have the highest P2P_ID
    if (j == 0) {
        succ_p2p_id = lesser_than[0];
        strncpy(succ_ip, lesser_ip[0], 256);
        succ_tcp = lesser_tcp[0];
        for (temp = 1; temp < i; temp++) {
            if (succ_p2p_id != lesser_than[temp]) {
                succ_p2p_id = MIN(succ_p2p_id, lesser_than[temp]);
                strncpy(succ_ip, lesser_ip[temp], 256);
                succ_tcp = lesser_tcp[temp];
            }
        }
    }

    // Get the numeric versions of the IP addresses.
    inet_aton(predec_ip, &pre_sockarr.sin_addr);
    inet_aton(succ_ip, &succ_sockarr.sin_addr);

    // Declare necessary variables and create P2P_JOIN_REQUEST packet.
    packetContents content;
    char arr[300];
    int len;
    content.messageType = P2P_JOIN_REQUEST;
    content.length = 0x08;
    content.server_p2p_id = p2p_id;
    len = assemblePacket(arr, &content);

    if (predec_p2p_id != 0) {
        // Predecessor exists; transmit to predecessor.

        // Get file descriptor for predecessor.
        predSocketFd = socket(PF_INET, SOCK_STREAM, 0);

        if (predSocketFd < 0) {
            fprintf(stderr, "!socket: Cannot create TCP socket for predecessor. \n");
            done(&currentPlayers, &backupPlayers, &history, EXIT_FAILURE);
        }

        maxSocketFd = MAX(maxSocketFd, predSocketFd);
        addBuffer(&currentBuffers, predSocketFd);
        numConnections++;

        struct sockaddr_in predTcpSin;

        memset(&predTcpSin, 0, sizeof (predTcpSin));
        predTcpSin.sin_family = AF_INET;
        predTcpSin.sin_addr.s_addr = htonl(pre_sockarr.sin_addr.s_addr);
        predTcpSin.sin_port = htons(predec_tcp);


        // PROBLEM AREA

        if (connect(predSocketFd, (struct sockaddr *) &predTcpSin,
                sizeof (predTcpSin)) < 0) {
            fprintf(stdout, "!connect: Cannot connect to predecessor. \n");
            done(&currentPlayers, &backupPlayers, &history, EXIT_FAILURE);
        } 

        
        sendBytes(predSocketFd, arr, len, tcpListener, &currentBuffers,
                &currentPlayers, &masterReadFds,
                &numConnections, &maxSocketFd);

    }

    if (succ_p2p_id != 0) {
        // Successor exists; transmit to successor.

        struct sockaddr_in succTcpSin;

        // However, do so only if the successor
        // and the predecessor are different.
        if (succ_p2p_id != predec_p2p_id) {

            // Get file descriptor for successor.
            succSocketFd = socket(PF_INET, SOCK_STREAM, 0);
            if (succSocketFd < 0) {
                fprintf(stderr, "!socket: Cannot create TCP socket for successor. \n");
                done(&currentPlayers, &backupPlayers, &history, EXIT_FAILURE);
            }
            maxSocketFd = MAX(maxSocketFd, succSocketFd);
            addBuffer(&currentBuffers, succSocketFd);
            numConnections++;

            memset(&succTcpSin, 0, sizeof (succTcpSin));
            succTcpSin.sin_family = AF_INET;
            succTcpSin.sin_addr.s_addr = htonl(succ_sockarr.sin_addr.s_addr);
            succTcpSin.sin_port = htons(succ_tcp);

            if (connect(succSocketFd, (struct sockaddr *) &succTcpSin,
                    sizeof (succTcpSin)) < 0) {
                fprintf(stdout, "!connect: Cannot connect to successor. \n");
                done(&currentPlayers, &backupPlayers, &history, EXIT_FAILURE);
            }


            sendBytes(succSocketFd, arr, len, tcpListener, &currentBuffers,
                    &currentPlayers, &masterReadFds,
                    &numConnections, &maxSocketFd);

        } else {
            succSocketFd = predSocketFd;
        }
    }

    while (1) {
        // Copy the master set into the temporary set.
        readFds = masterReadFds;
        timeout.tv_sec = 0;
        timeout.tv_usec = 500000;

        if (select(maxSocketFd + 1, &readFds, NULL, NULL, &timeout) < 0) {
            fprintf(stdout, "!select: Call to `select' failed. \n");
            done(&currentPlayers, &backupPlayers, &history, EXIT_FAILURE);
        }

        // Run through the existing connections looking for data to read.
        int i;
        for (i = 0; i <= maxSocketFd; i++) {

            if (FD_ISSET(i, &readFds)) {
                if (i == tcpListener) {
                    if (numConnections >= MAX_CONNECTIONS) {
                        fprintf(stdout, "Reached maximum connections. \n");
                    } else {
                        // Listener has an incoming connection; accept it.
                        int newSocketFd = accept(tcpListener,
                                (struct sockaddr *) & newTcpSin,
                                &newTcpSinSize);

                        if (newSocketFd != -1) {
                            // Add to master set.
                            FD_SET(newSocketFd, &masterReadFds);

                            // Reset maximum, if need be.
                            maxSocketFd = MAX(newSocketFd, maxSocketFd);

                            // Assign a buffer to this file descriptor.
                            addBuffer(&currentBuffers, newSocketFd);

                            numConnections++;
                        }
                    }
                } else if (i == udpListener) {
                    // Deal with data from a UDP port.
                    processUdpBytes(udpListener, &history);
                } else {
                    // Deal with data from a client through a TCP port.
                    processTcpBytes(i, tcpListener, &currentBuffers,
                            &currentPlayers, &backupPlayers, &masterReadFds,
                            &numConnections, &maxSocketFd, &predSocketFd,
                            &succSocketFd, &history, p2p_id);
                }
            }
        }
    }

    done(&currentPlayers, &backupPlayers, &history, EXIT_SUCCESS);
};
